Preserve transparency when padding and expanding images - #9995
Conversation
|
Thank you for catching this. You're right that the one-entry palette with two transparency bytes does not represent a valid PNG input. I overlooked that constraint, and this fixture should not have been used to justify the allocation change. I rechecked the original issue using valid PNGs saved and reopened before the operation. With a two-entry palette (black and red) and transparency index 0, Would you prefer that I remove the ImagePalette changes and the out-of-range fixtures, and keep this PR focused on preserving transparency in ImageOps? I can leave full-palette allocation behavior for a separate discussion. Sorry for overlooking the invalid fixture, and thank you for the careful review. |
|
I'm not sure what you meant by 'full-palette allocation behavior', but I've pushed some commits to remove the ImagePalette changes and simplify the tests. If you approve, then I'll merge this and you can create another issue or PR for anything further you'd like. |
|
Thanks for simplifying this, and sorry my earlier explanation was unclear. I'm happy with the updated ImageOps fix and the simplified tests. Please go ahead and merge this. Thanks again for your help and patience. |
|
Don't worry about it. Thanks for finding this issue. |
ImageOps.pad()andImageOps.expand()discardinfo["transparency"]when creating an image with a border. Transparent pixels consequently become opaque in P, L and RGB images, including when the result is saved as PNG.Changes proposed in this pull request:
_new_with_fill()helper and pass the source image when allocating a new palette color, so reserved transparency indices are not reused for the border.Reproduction:
Validation:
main: 15 failed, 90 passed. Twelve failures show the output pixels losing transparency; three cover allocation into transparent palette entries.Tests/test_imageops.py,Tests/test_imagepalette.py,Tests/test_imagecolor.pyandTests/test_imagestat.py: 105 passed, no skips. The existingTests/test_imagedraw.py::test_new_coloralso passes;selftest.py: 59 passed.Tests ran on Windows/Python 3.12.11 using the checkout's
ImageOps,ImagePalette,ImageColorandImageStatPython modules with Pillow 12.3.0's prebuilt native extensions and remaining Python modules. Native extensions were not rebuilt; the full test suite and full pre-commit suite were not run.Prepared with AI assistance.
CI follow-up (2026-09-14): The Windows Python 3.13 and 3.14 x64 checks passed on the maintainer-triggered second attempt of run 34751277259, on the unchanged commit 2d53304. All 53 reported checks passed before the release-note follow-up. The earlier PNG decoding worker crashes have not been root-caused; a successful rerun does not establish that the underlying cause has been fixed. No source or tests were changed to suppress those failures. On the documentation-only follow-up de0892e, Windows Python 3.14 failed again in the same two RGB round-trip tests (2 failed, 4107 passed), while Python 3.13 passed. The failure remains unresolved; this PR remains in draft.
CI comparison (2026-09-14): The passing Windows 3.14 rerun and the subsequent failure both used Python 3.14.7, Windows image
20260907.229.1, zlib-ng 2.3.3, pytest 9.1.1, pytest-cov 7.1.0 and pytest-xdist 3.8.0 with four workers. Their actual checked-out merge commits,f15c554e3d80b79288707cb6dc0e7051f4af45b6and8c12e4c14b7121d5f34b17d413c2fd3bf1a73729, have identical top-level Git tree entries exceptdocs, including identical source, tests, build and CI configuration. Both also checked out cached dependencies at0a1e459d4dbd36ef4f6b411124441f96bdef1110. Static review found the PNG save/seek/open sequence and buffer lifetime consistent with the existing PNG tests. This narrows the comparison but does not identify the crash cause; no tests or dependencies have been changed to suppress it.